﻿/*   
    Copyright (C) 2009 Galaktika Corporation ZAO

    This file is a part of Galaktika.BusinessMonitor
 
    Galaktika.BusinessMonitor is a free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
      
    You should have received a copy of the GNU General Public License
    along with Galaktika.BusinessMonitor.  If not, see <http://www.gnu.org/licenses/>.
  
    If GPL v.3 is not suitable for your products or company,
    Galaktika Corp provides Galaktika.BusinessMonitor under a flexible commercial license
    designed to meet your specific usage and distribution requirements.
    If you have already obtained a commercial license from Galaktika Corp,
    you can use this file under those license terms.
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;

namespace Galaktika.BI.Addin.VisifireChart
{
//[TypeConverter(typeof(TypeConverter)), DesignerSerializer("DevExpress.XtraCharts.Design.ChartItemSerializer,DevExpress.XtraCharts.v9.1", "System.ComponentModel.Design.Serialization.CodeDomSerializer")]

    public enum PaletteScaleMode
    {
        Repeat,
        Extrapolate
    }
    [Serializable,TypeConverter(typeof(Palette.TypeConverter2))]
    public class Palette : CollectionBase, ICloneable
    {
        // Fields
        internal static readonly Color EmptyPaletteColor = Color.Silver;
        private string name;
        private bool predefined;
        private PaletteScaleMode scaleMode;

        // Methods        
        public Palette(string name)
            : this(name, PaletteScaleMode.Repeat)
        {
            this.name = name;
        }

        public Palette(string name, PaletteEntry[] entries)
            : this(name, PaletteScaleMode.Repeat, entries)
        {
        }

        public Palette(string name, PaletteScaleMode scaleMode)
        {
            this.name = name;
            this.scaleMode = scaleMode;
        }

        public Palette(string name, PaletteScaleMode scaleMode, PaletteEntry[] entries)
            : this(name, scaleMode)
        {
            for (int i = 0; i < entries.Length; i++)
            {
                this.Add(entries[i]);
            }
        }

        public int Add(PaletteEntry entry)
        {
            if (entry == null)
            {
                throw new ArgumentNullException("entry");
            }
            this.TestPredefinedFlag();
            entry.SetPalette(this);
            return base.InnerList.Add(entry);
        }

        public int Add(Color color)
        {
            return this.Add(color, color);
        }

        public int Add(Color color, Color color2)
        {
            return this.Add(new PaletteEntry(color, color2));
        }

        internal void AddDefaultColorIfEmpty()
        {
            if (base.Count == 0)
            {
                this.Add(new PaletteEntry());
            }
        }

        public void Assign(Palette palette)
        {
            if (palette == null)
            {
                throw new ArgumentNullException("palette");
            }
            this.TestPredefinedFlag();
            this.name = palette.name;
            base.InnerList.Clear();
            int count = palette.Count;
            for (int i = 0; i < count; i++)
            {
                this.Add((PaletteEntry)palette[i].Clone());
            }
        }

        public object Clone()
        {
            Palette palette = new Palette(string.Empty);
            palette.Assign(this);
            return palette;
        }

        private static Color ConvertColor(Color color, int cycleIndex, int cycleCount)
        {
            ColorHSL rhsl = (ColorHSL)color;
            float num = ((float)(cycleCount - 1)) / ((float)cycleCount);
            float minLuminance = rhsl.Luminance - (num * 0.5f);
            if (minLuminance < rhsl.MinLuminance)
            {
                minLuminance = rhsl.MinLuminance;
            }
            float maxLuminance = rhsl.Luminance + (num * 0.5f);
            if (maxLuminance > rhsl.MaxLuminance)
            {
                maxLuminance = rhsl.MaxLuminance;
            }
            float num4 = ((float)(cycleCount - 1)) / 2f;
            float num5 = cycleIndex - num4;
            if (num5 < 0f)
            {
                rhsl.Luminance -= (minLuminance - rhsl.Luminance) * (num5 / num4);
            }
            else
            {
                rhsl.Luminance += (maxLuminance - rhsl.Luminance) * (num5 / num4);
            }
            return (Color)rhsl;
        }

        internal Image CreateEditorImage()
        {
            Bitmap image = null;
            try
            {
                image = new Bitmap((base.Count * 11) - 1, 10);
                using (Graphics graphics = Graphics.FromImage(image))
                {
                    Rectangle rect = new Rectangle(Point.Empty, new Size(10, 10));
                    int num = 0;
                    while (num < base.Count)
                    {
                        using (Brush brush = new SolidBrush(this[num].Color))
                        {
                            graphics.FillRectangle(brush, rect);
                        }
                        Rectangle rectangle2 = rect;
                        rectangle2.Width--;
                        rectangle2.Height--;
                        using (Pen pen = new Pen(Color.Gray))
                        {
                            graphics.DrawRectangle(pen, rectangle2);
                        }
                        num++;
                        rect.X += 11;
                    }
                    return image;
                }
            }
            catch
            {
                if (image != null)
                {
                    image.Dispose();
                    image = null;
                }
            }
            return image;
        }

        internal static Palette CreatePredefinedPalette(string resourceName)
        {
            Palette palette = new Palette(string.Empty);
            XmlTextReader xmlReader = new XmlTextReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName));
            try
            {
                palette.ReadFromXml(xmlReader);
            }
            finally
            {
                xmlReader.Close();
            }
            palette.predefined = true;
            return palette;
        }

        internal PaletteEntry GetEntry(int index, int count, int baseColorNumber)
        {
            if (baseColorNumber != 0)
            {
                PaletteEntry entry = this.GetEntryByCycle(baseColorNumber - 1);
                if (count == 1)
                {
                    return entry;
                }
                Color color = ConvertColor(entry.Color, index, count);
                return new PaletteEntry(color, ConvertColor(entry.Color2, index, count));
            }
            PaletteEntry entryByCycle = this.GetEntryByCycle(index);
            if (this.scaleMode == PaletteScaleMode.Extrapolate)
            {
                int cycleCount = ((count - 1) / base.Count) + 1;
                if (cycleCount > 1)
                {
                    int cycleIndex = index / base.Count;
                    Color color3 = ConvertColor(entryByCycle.Color, cycleIndex, cycleCount);
                    return new PaletteEntry(color3, ConvertColor(entryByCycle.Color2, cycleIndex, cycleCount));
                }
            }
            return entryByCycle;
        }

        private PaletteEntry GetEntryByCycle(int index)
        {
            return this[index % base.Count];
        }

        protected override void OnClear()
        {
            this.TestPredefinedFlag();
        }

        protected override void OnRemove(int index, object value)
        {
            this.TestPredefinedFlag();
        }

        private void ReadFromXml(XmlTextReader xmlReader)
        {
            xmlReader.ReadStartElement("Palette");
            this.name = xmlReader.ReadElementString("Name");
            //this.scaleMode = (PaletteScaleMode)XmlUtils.ReadEnum(xmlReader, "ScaleMode", typeof(PaletteScaleMode));
            xmlReader.ReadStartElement("Items");
            base.InnerList.Clear();
            //int num = XmlUtils.ReadInteger(xmlReader, "Count");
            //for (int i = 0; i < num; i++)
            //{
            //    PaletteEntry entry = new PaletteEntry();
            //    entry.ReadFromXml(xmlReader);
            //    this.Add(entry);
            //}
            xmlReader.ReadEndElement();
            xmlReader.ReadEndElement();
        }

        internal void SetName(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("name");
            }
            this.name = name;
        }

        internal void SetPredefinedFlag()
        {
            this.predefined = true;
        }

        internal void SetScaleMode(PaletteScaleMode scaleMode)
        {
            this.scaleMode = scaleMode;
        }

        internal void TestPredefinedFlag()
        {
            if (this.predefined)
            {
               // throw new PaletteException(ChartLocalizer.GetString(ChartStringId.MsgModifyDefaultPaletteError));
            }
        }

        public override string ToString()
        {
            return this.name;
        }

        private void WriteToXml(XmlTextWriter xmlWriter)
        {
            xmlWriter.WriteStartElement("Palette");
            xmlWriter.WriteElementString("Name", this.name);
            //XmlUtils.WriteEnum(xmlWriter, this.scaleMode, "ScaleMode");
            xmlWriter.WriteStartElement("Items");
            xmlWriter.WriteElementString("Count", base.Count.ToString());
            for (int i = 0; i < base.Count; i++)
            {
                this[i].WriteToXml(xmlWriter);
            }
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndElement();
        }

        // Properties
        internal bool IsEmpty
        {
            get
            {
                return ((base.Count == 1) && this[0].IsEmpty);
            }
        }

        [Extensibility.Description("Provides indexed access to individual items in the palette.")]
        public PaletteEntry this[int index]
        {
            get
            {
                return (PaletteEntry)base.InnerList[index];
            }
        }

        [Extensibility.Description("Gets the name of the palette.")]
        public string Name
        {
            get
            {
                return this.name;
            }
        }

        [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
        public string NameSerializable
        {
            get
            {
                return this.Name;
            }
            set
            {
                this.SetName(value);
            }
        }

        [Extensibility.Description("Reports whether the palette is user-defined or built-in.")]
        public bool Predefined
        {
            get
            {
                return this.predefined;
            }
        }

        [Extensibility.Description("Gets the value used to specify the method for extending the color set of the palette.")]
        public PaletteScaleMode ScaleMode
        {
            get
            {
                return this.scaleMode;
            }
        }

        [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
        public PaletteScaleMode ScaleModeSerializable
        {
            get
            {
                return this.ScaleMode;
            }
            set
            {
                this.SetScaleMode(value);
            }
        }

        // Nested Types
        internal class ColorHSL
        {
            // Fields
            private float hue;
            private float luminance;
            private const float maxLuminance = 0.8f;
            private const float minLuminance = 0.5f;
            private float saturation;

            // Methods
            public ColorHSL(float hue, float saturation, float luminance)
            {
                this.hue = hue;
                this.saturation = saturation;
                this.luminance = luminance;
            }

            private static byte GetComponent(float q, float p, float t)
            {
                float num;
                while (t < 0f)
                {
                    t++;
                }
                while (t > 1f)
                {
                    t--;
                }
                if (t < 0.1666667f)
                {
                    num = p + (((q - p) * 6f) * t);
                }
                else if (t < 0.5f)
                {
                    num = q;
                }
                else if (t < 0.6666667f)
                {
                    num = p + (((q - p) * (0.6666667f - t)) * 6f);
                }
                else
                {
                    num = p;
                }
                return (byte)Math.Round((double)(num * 255f));
            }

            public static explicit operator Color(Palette.ColorHSL color)
            {
                float q = (color.luminance < 0.5f) ? (color.luminance * (1f + color.saturation)) : ((color.luminance + color.saturation) - (color.luminance * color.saturation));
                float p = (2f * color.luminance) - q;
                float num3 = color.hue / 360f;
                float t = num3 + 0.3333333f;
                float num5 = num3;
                float num6 = num3 - 0.3333333f;
                return Color.FromArgb(0xff, GetComponent(q, p, t), GetComponent(q, p, num5), GetComponent(q, p, num6));
            }

            public static explicit operator Palette.ColorHSL(Color color)
            {
                return new Palette.ColorHSL(color.GetHue(), color.GetSaturation(), color.GetBrightness());
            }

            // Properties
            public float Hue
            {
                get
                {
                    return this.hue;
                }
                set
                {
                    this.hue = value;
                }
            }

            public float Luminance
            {
                get
                {
                    return this.luminance;
                }
                set
                {
                    this.luminance = value;
                }
            }

            public float MaxLuminance
            {
                get
                {
                    float num = this.luminance + ((1f - this.luminance) * 0.15f);
                    if (0.8f <= num)
                    {
                        return num;
                    }
                    return 0.8f;
                }
            }

            public float MinLuminance
            {
                get
                {
                    float num = this.luminance * 0.9f;
                    if (0.5f >= num)
                    {
                        return num;
                    }
                    return 0.5f;
                }
            }

            public float Saturation
            {
                get
                {
                    return this.saturation;
                }
                set
                {
                    this.saturation = value;
                }
            }
        }

        internal class TypeConverter2 : TypeConverter
        {
            // Methods
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                if (destinationType != typeof(InstanceDescriptor))
                {
                    return base.CanConvertTo(context, destinationType);
                }
                return true;
            }

            public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
            {
                if (destinationType != typeof(InstanceDescriptor))
                {
                    return base.ConvertTo(context, culture, value, destinationType);
                }
                return new InstanceDescriptor(typeof(Palette).GetConstructor(new Type[] { typeof(string), typeof(PaletteScaleMode), typeof(PaletteEntry[]) }), this.GetConstructorParams(value), true);
            }

            private object[] GetConstructorParams(object value)
            {
                Palette palette = (Palette)value;
                object[] objArray = new object[3];
                objArray[0] = palette.Name;
                objArray[1] = palette.ScaleMode;
                int count = palette.Count;
                PaletteEntry[] entryArray = new PaletteEntry[count];
                for (int i = 0; i < count; i++)
                {
                    entryArray[i] = (PaletteEntry)palette[i].Clone();
                }
                objArray[2] = entryArray;
                return objArray;
            }
        }

        private class XmlKeys
        {
            // Fields
            public const string Count = "Count";
            public const string Items = "Items";
            public const string Name = "Name";
            public const string Palette = "Palette";
            public const string ScaleMode = "ScaleMode";
        }
    }

    [TypeConverter(typeof(TypeConverter2))]
    [Serializable]
    public class PaletteEntry : ICloneable
    {
        // Fields
        private Color color;
        private Color color2;
        private Palette palette;

        // Methods
        public PaletteEntry()
            : this(Palette.EmptyPaletteColor)
        {
        }

        public PaletteEntry(Color color)
            : this(color, color)
        {
        }

        public PaletteEntry(Color color, Color color2)
        {
            this.color = color;
            this.color2 = color2;
        }

        public virtual void Assign(PaletteEntry entry)
        {
            if (entry == null)
            {
                throw new ArgumentNullException("entry");
            }
            this.TestPredefinedFlag();
            this.color = entry.color;
            this.color2 = entry.color2;
        }

        public object Clone()
        {
            PaletteEntry entry = new PaletteEntry();
            entry.Assign(this);
            return entry;
        }

        internal void ReadFromXml(XmlTextReader xmlReader)
        {
            xmlReader.ReadStartElement("Entry");
            //this.color = XmlUtils.ReadColor(xmlReader, "Color");
            //this.color2 = XmlUtils.ReadColor(xmlReader, "Color2");
            xmlReader.ReadEndElement();
        }

        internal void SetPalette(Palette palette)
        {
            this.palette = palette;
        }

        private void TestPredefinedFlag()
        {
            if (this.palette != null)
            {
                this.palette.TestPredefinedFlag();
            }
        }

        internal void WriteToXml(XmlTextWriter xmlWriter)
        {
            xmlWriter.WriteStartElement("Entry");
            //XmlUtils.WriteColor(xmlWriter, this.color, "Color");
            //XmlUtils.WriteColor(xmlWriter, this.color2, "Color2");
            xmlWriter.WriteEndElement();
        }

        // Properties
        //[Extensibility.Description("Gets or sets the first color of the palette entry."), XtraSerializableProperty]        
        public Color Color
        {
            get
            {
                return this.color;
            }
            set
            {
                this.TestPredefinedFlag();
                this.color = value;
            }
        }

       // [XtraSerializableProperty, Extensibility.Description("Gets or sets the second color of the palette entry.")]  
        public Color Color2
        {
            get
            {
                return this.color2;
            }
            set
            {
                this.TestPredefinedFlag();
                this.color2 = value;
            }
        }

        internal bool IsEmpty
        {
            get
            {
                return ((this.color == Palette.EmptyPaletteColor) && (this.color2 == Palette.EmptyPaletteColor));
            }
        }

        internal Palette Palette
        {
            get
            {
                return this.palette;
            }
        }

        // Nested Types
        internal class TypeConverter2 : TypeConverter
        {
            // Methods
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                return ((destinationType == typeof(InstanceDescriptor)) || base.CanConvertTo(context, destinationType));
            }

            public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
            {
                if (destinationType == typeof(InstanceDescriptor))
                {
                    return new InstanceDescriptor(typeof(PaletteEntry).GetConstructor(new Type[] { typeof(Color), typeof(Color) }), this.GetConstructorParams(value), true);
                }
                return base.ConvertTo(context, culture, value, destinationType);
            }

            private object[] GetConstructorParams(object value)
            {
                PaletteEntry entry = (PaletteEntry)value;
                return new object[] { entry.Color, entry.Color2 };
            }
        }

        private class XmlKeys
        {
            // Fields
            public const string Color = "Color";
            public const string Color2 = "Color2";
            public const string Entry = "Entry";
        }
    }  

}
